home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Toolbox / Live Scroll 1.0 / Documentation / ReadMe- Live Scroll next >
Encoding:
Text File  |  1996-05-13  |  14.1 KB  |  174 lines  |  [ttro/ttxt]

  1. Live Scroll
  2.  
  3.  
  4.  
  5. Written by:    Chris White,  Developer Technical Support
  6.     
  7. Copyright:    © 1996 by Apple Computer, Inc. All rights reserved.
  8.  
  9.  
  10.  
  11. What it does
  12.  
  13. "Live Scroll" is a bare bones application demonstrating one approach you can take to implementing live scrolling (aka active and dynamic scrolling) during the tracking of scroll bar thumbs. Scroll arrows allow accurate placement of a document within its window, but are often too slow when the user wishes to scroll the content relatively large distances. While the scroll thumb can be used in this situation, it is less than ideal because the user cannot see the result of the scroll until the thumb is released. As a result, the user often finds that two or more scroll operations are required before the desired positioning is achieved. These problems can be overcome by implementing live scrolling within an application which allows a user to more accurately scroll a document to the correct position with direct control over the speed of the scrolling, and by providing complete visual feedback of the scrolling operation as it occurs.
  14.  
  15. It also demonstrates:
  16.     -The two different types of action procedure, both for 68K and PowerPC architectures.
  17.     -Use of the new accessor routines which are provided as the first step to being Copland-savvy.
  18.  
  19.  
  20.  
  21. How to use the Application
  22.  
  23. It's fairly obvious, but to use the application just click on one of the scroll bar thumbs and drag as usual. Rather than having an outline of the thumb, you'll be dragging the real thumb and the window content will scroll immediately. The same behaviour as the standard thumb tracking has been observed, and positioning the mouse too far away from the scroll bar will result in the scroll returning to its original position.
  24.  
  25.  
  26.  
  27. Limitations and Bugs
  28.  
  29. This is not intended to be a definitive 'document' on how to implement live scrolling, but illustrates one approach you can take. Like most projects, this project was implemented under time constraints. There may be better methods available to you depending on your needs and the available time.
  30.  
  31. I'm not aware of any bugs, so if you find any then _please_ let me know.
  32.  
  33.  
  34.  
  35. Building
  36.  
  37. Live Scroll has been built under:
  38.     Metrowerks CodeWarrior 8
  39.     Symantec C++ 8.0.1
  40.     Symantec 7.0.4
  41.     MPW E.T.O. #19- 'Latest MPW': Symantec C++ for MPW and MrC.
  42.  
  43. The Symantec environments are using a slightly older version of the Universal Interfaces than MPW and CodeWarrior. Because the source code uses some of the new accessor routines which are provided as the first step to being Copland-savvy, you will need to use a later version of the Universal Interfaces than is provided with the Symantec products, or make some changes to the source code. To change the Universal Interfaces, simply place brackets around the existing folder and place the folder containing the later version into the same folder as the existing ones. The brackets will prevent the development environment from using the files contained within the old folder.
  44.  
  45. The new accessor routines will allow you to remove direct accesses to some of the toolbox data structures, at a source code level. Currently, these accessors are implemented as macros. However, they will be available as true API entry points in Copland.
  46.  
  47. Two MPW make files are included: to build a 68K and a fat version using 'Latest MPW'. The make files have been set up to use Symantec C++ for MPW  (SC) and MrC, together with the interfaces and libraries from 'Latest MPW'.
  48.  
  49.  
  50.  
  51. Strategies
  52.  
  53.  
  54. Tracking Context
  55.  
  56. Since this version of the action procedure has no parameters, some method of storing and retrieving the context of the track is needed. This context information can be stored using global data or, for the most part, hung off the control's reference constant. Since the action procedure is tied directly to a user interface element being used, it is not necessary to eliminate the use global data to make the code reentrant. It just isn't possible for this code to be used at the same time in different threads, or in any other reentrant manner.
  57.  
  58. For simplicity, the tracking context is stored in global data. The problems that can result from using global data can largely be avoided if thought about. In this case, the static keyword is used to limit the scope of the global data to a single file, namely ScrollBars.c. An accessor routine must then be used outside of this source file when referencing the global data. The BeginThumbTracking routine takes a ControlRef as a parameter, and is called just prior to the thumb tracking taking place. This ControlRef is assigned to a global variable to which the other routines involved in the tracking process have access. Other global variables are used internally to the ScrollBars.c source file, and do not need accessor routines. This global data constitutes the context for the action procedure during the tracking.
  59.  
  60.  
  61. Integer Data Types
  62.  
  63. To help keep the source code portable and avoid problems caused by compiler and architecture changes, the source code uses the integer typedefs defined by the Universal Interfaces in Types.h. For example:-
  64.  
  65. typedef unsigned short        UInt16;
  66. typedef signed short                SInt16;
  67. typedef unsigned long            UInt32;
  68. typedef signed long                    SInt32;
  69.  
  70. Although C purists may argue that for truly portable code the int data should be used extensively, experience has shown that using this data type throughout will cause more problems than not using it. As a general guideline, the int data type can be used when the size of the integer is only dependant on the rest of your own source code. The Apple defined typedefs can be used in all other cases. Specifically, when the size of the integer is dependant on source code outside of your control, for example when passing the address of an integer to a toolbox routine.
  71.  
  72.  
  73. Error Handling
  74.  
  75. Although there is very little need for error handling in the demonstration application, there are two guidelines that have been used to try and avoid the normal sort of problems that can occur as the result of an error exception. First, each routine cleans up after itself. Second, each routine leaves everything in the same state as it found it in.
  76.  
  77. Having each routine clean up after itself can lead to a number of difficulties in C as far as readability and maintainability are concerned. A block of code dedicated to cleaning up and returning from a routine can be a few lines in length. Duplicating this after each error code leads to inconsistencies, errors, and reduced readability. It's worth using C++ just for the exception handling features, but if you must use C you may want to use a similar approach to this. On occasion, we've used a goto statement to jump to a block of code at the end of the routine. If the clean up code should only be called as the result of an exception, the routine returns before dropping into this clean up code.
  78.  
  79. If the current port or resource file is changed, restore them before leaving the routine. Pay particular attention to this when an error occurs because this is usually the place where it's likely to be overlooked. If a handle needs to be in a particular state, save its current state using the HGetState routine before changing it. The HSetState routine can then be used to restore it, and the problems associated with a routine unintentional changing the state of a handle can be avoided.
  80.  
  81. Runtime errors that should never occur in a bug free program can use a DebugStr call. These can be conditionally compile using a #define. For example, our ScrollThumbActionProc routine first validates the theInfo variable. This routine should never be called when the window does not have a valid refCon and, therefore, a valid theInfo variable, so we use the following code snippet to prevent the application from crashing, and we can also get an idea of what may have gone wrong. 
  82.  
  83.     #if DEBUGGING
  84.     if ( theInfo == nil ) DebugStr ( "\p theInfo == nil" );
  85.     #endif
  86.  
  87.  
  88.  
  89. Data Storage
  90.  
  91. The data structure of type tWindowInfo is used internally to store the data associated with each window. It's allocated using the NewPtrClear routine and the pointer is then assigned to the window refCon field. The ControlRef values for the horizontal and vertical scroll bars and the offscreen graphics world are assigned to the relevant fields within this data structure. It's defined as follows:-
  92.  
  93.  
  94. struct WindowInfo
  95. {
  96.     ControlRef    hScrollBar;
  97.     ControlRef    vScrollBar;
  98.     GWorldPtr    offscreen;
  99. };
  100.  
  101. typedef struct WindowInfo tWindowInfo, *tWindowInfoPtr;
  102.  
  103.  
  104. As already discussed, the tracking context is stored in global data. The ControlRef global is used to keep track of which control is being tracked. It's value is assigned just prior to the start of thumb tracking, in the BeginThumbTracking routine. In addition, this routine calculates the value of gValueSlop to account for the discrepancy between the actual point clicked within the scroll thumb and the absolute centre of the thumb which is assumed in subsequent calculations. The gSaveValue global is used to store the final value of the control after tracking has ended, and the gStartValue global is a copy of the control value from before tracking begins. Finally, the gSaveClip global is used to save the clipping region of the window before tracking begins. It is restored when tracking has ended. This data is further explained in the next section, and is defined as follows:-
  105.  
  106.  
  107. static ControlRef            gControl;
  108. static SInt32                            gValueSlop;
  109. static SInt32                            gSaveValue;
  110. static SInt32                            gStartValue;
  111. static RgnHandle                gSaveClip = nil;
  112.  
  113.  
  114.  
  115. How the Interesting Stuff Works
  116.  
  117.  
  118. Action Procedures
  119.  
  120. Although there's a considerable amount of information on implementing scroll bars in Inside Macintosh, the documentation on action procedures is not quite so clear. TrackControl actually takes two kinds of action procedures, depending on whether you get an indicator partcode or an ordinary partcode from the FindControl or TestControl toolbox routines. For an ordinary partcode such as inUpButton, inDownButton, inPageUp, and inPageDown, the action procedure is declared as:
  121.  
  122. pascal void myAction ( ControlHandle ctlHan, short partCode );
  123.  
  124. For an indicator like inThumb, the callback is declared as:
  125.  
  126. pascal void myAction ( void );
  127.  
  128.  
  129. Unfortunately, at this time there is no predefined Universal Procedure Pointer macros for the indicator version of the action procedure. I've put together the necessary macros, and included them in the ScrollBars.h source file for completeness. However, since the DragGrayRgn routine uses the same prototype, you can also use DragGrayRgnUPP and the associated macros which are defined in Quickdraw.h
  130.  
  131.  
  132. Calculating the control value
  133.  
  134. The basic function of the action procedure is to calculate a new value for the control, based on the mouse location. Once this has been achieved and the control set to this new value, the process of actually scrolling the document is well know and documented, and is already required for normal scrolling support. Therefore, I will concentrate on calculating the control value.
  135.  
  136. First, the GetMouse toolbox routine is used to find the mouse location. A ratio of the mouse to the total scroll bar distance is then calculated. This ratio can then be applied to the range of values in the control, which are easily obtained using the GetControlMinimum and GetControlMaximum toolbox routines. The CalcValueFromPoint routine performs this task and can be found in the ScrollBars.c source file. As already mentioned, there is one complication in this, which is that the calculations assume the point returned from the GetMouse routine is the absolute centre of the scroll thumb. Since the user is not likely to have clicked in the absolute centre, we need to find some way of determining how far off the centre the user did click, or how much our calculation of the control value will be wrong as a result. While the first is not too easy to obtain, the latter can easily be found. We simply calculate the current value of the control based on the current mouse location before the tracking is started, and subtract it from the real current value of the control. This value can then be added to subsequent calculations to adjust the value for the discrepancy. This is performed in the BeginThumbTracking routine and stored in gValueSlop, which is subsequently added to each result calculated in the CalcValueFromPoint routine.
  137.  
  138. One final point regarding the control value, if the user drags too far away from the scroll bar area the control value is restored to the value before the control tracking began. This starting value is obtained in the  BeginThumbTracking routine, and assigned to the gStartValue global.
  139.  
  140.  
  141. Caveats
  142.  
  143. There are two caveats that need to be dealt with. First, the TrackControl toolbox routine assigns the control value after the action procedure has been called for the last time, and before returning to the application. Unfortunately, its idea of the correct control value is not the same as ours, so we have to save the control value as calculated in the action procedure, and reset the control value after TrackControl has returned to the application. The gStartValue global is used for this purpose, and is reassigned to the control by the EndThumbTracking routine. The second caveat is that during the tracking of the thumb, the normal thumb outline will be drawn as usual just out of synch with the actual thumb. In addition, the final thumb will be redrawn based on the incorrect control value as discussed in the first caveats. To work around both of these problems, we set the clipping region to a null rectangle to disable all drawing while the TrackControl routine is executing. The gSaveClip global is used to save the original clipping region of the window so that we may restore it correctly later.
  144.  
  145.  
  146.  
  147. References
  148.  
  149. Inside Macintosh: Macintosh Toolbox Essentials, Chapter 5 “Control Manager”, Page 115 “Defining Your Own Action Procedures”
  150.  
  151. Inside Macintosh: PowerPC System Software, Chapter 1 “Introduction to PowerPC System Software”, Page 15 “Routine Descriptors”
  152.  
  153.  
  154.  
  155. Additional functionality to add
  156.  
  157. -Sorry, can't think of anything.
  158.  
  159.  
  160.  
  161. Bugs, Comments and Suggestions
  162.  
  163. Bug reports, comments and suggestions should be made to the AppleLink address DEVSUPPORT and marked for my attention.
  164.  
  165.  
  166. Enjoy!
  167.  
  168. Chris White
  169. Developer Technical Support
  170. March '96
  171.  
  172. © 1996 by Apple Computer, Inc. All rights reserved.
  173.  
  174.